

package com.googlecode.flac4j.format.frame.sub;


import com.googlecode.jinahya.io.BitInput;
import com.googlecode.jinahya.io.BitOutput;
import com.googlecode.jinahya.util.BitAccessible;

import java.io.IOException;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;


/**
 *
 * @author Jin Kwon <jinahya at gmail.com>
 */
@XmlType(propOrder = {"method", "rice"})
public class Residual implements BitAccessible {


    @Override
    public void read(final BitInput input) throws IOException {

        setMethod(input.readUnsignedInt(2));

        if (rice != null) {
            rice.setResidual(this);
            rice.read(input);
        }
    }


    @Override
    public void write(final BitOutput output) throws IOException {

        output.writeUnsignedInt(2, method);

        rice.write(output);
    }


    // ---------------------------------------------------------- parent SubData
    /**
     * Returns the parent SubData of this Residual.
     *
     * @return parent subData
     */
    public final SubData getSubData() {
        return parent;
    }


    /**
     * Sets the parent SubData of this Residual.
     *
     * @param parent parent SubData
     */
    public final void setSubData(final SubData parent) {

        if (parent == null) {
            throw new NullPointerException("null subData");
        }

        this.parent = parent;
    }


    // ------------------------------------------------------------------ method
    /**
     * Returns method.
     *
     * @return method
     */
    public int getMethod() {

        if (method < 0) {
            throw new IllegalStateException("method unset");
        }

        return method;
    }


    /**
     * Sets method.
     *
     * @param method method
     */
    public void setMethod(final int method) {

        if (method >> 2 != 0) {
            throw new IllegalArgumentException("method(" + method + ") != 0");
        }

        switch (method) {
            case 0x00:
                if (!(rice instanceof Rice1)) {
                    rice = new Rice1();
                }
                break;
            case 0x01:
                if (!(rice instanceof Rice2)) {
                    rice = new Rice2();
                }
                break;
            default:
                rice = null;
                break;
        }

        this.method = method;
    }


    // -------------------------------------------------------------------- rice
    /**
     * Returns rice.
     *
     * @return rice
     */
    public Rice getRice() {
        return rice;
    }


    /**
     * Sets rice.
     *
     * @param rice rice
     */
    public void setRice(final Rice rice) {

        if (rice == null) {
            throw new NullPointerException("null rice");
        }

        method = rice.getResidualMethod();
        this.rice = rice;
    }


    /** parent SubData. */
    @XmlTransient
    private SubData parent;


    /** method. */
    @XmlElement(required = true)
    private int method;


    @XmlElement(required = true)
    private Rice rice;


}

